package top.lshaci.learning.jvm.string;

import org.junit.Test;

/**
 * StringConcat
 *
 * @author lshaci
 * @date 2020/7/16 14:22
 * @since 1.0.0
 */
public class StringConcatTest {

    @Test
    public void test1() {
        String s1 = "a" + "b" + "c";
        String s2 = "abc"; // 字面量赋值，"abc"一定放在字符串常量池中，将地址赋值给s2
        /**
         * 最终.java编译成.class, 再执行.class
         * String s1 = "abc";
         * String s2 = "abc";
         */
        System.err.println(s1 == s2); // true
        System.err.println(s1.equals(s2)); // true
    }

    @Test
    public void test2() {
        String s1 = "javaEE";
        String s2 = "hadoop";

        String s3 = "javaEEhadoop";
        String s4 = "javaEE" + "hadoop"; // 编译期优化
        /**
         * 如果拼接符号的前后出现了变量，则相当于在堆空间中new String()，具体内容为拼接后的结果。
         */
        String s5 = s1 + "hadoop";
        String s6 = "javaEE" + s2;
        String s7 = s1 + s2;

        System.err.println(s3 == s4); // true
        System.err.println(s3 == s5); // false
        System.err.println(s3 == s6); // false
        System.err.println(s3 == s7); // false
        System.err.println(s5 == s6); // false
        System.err.println(s5 == s7); // false
        System.err.println(s6 == s7); // false

        /**
         * intern(): 字符串常量池中有就返回地址，没有就创建后返回地址
         */
        String s8 = s6.intern();
        System.err.println(s3 == s8); // true
    }

    @Test
    public void test3() {
        /**
         *  0 ldc #14 <a>
         *  2 astore_1
         */
        String s1 = "a";
        /**
         *  3 ldc #15 <b>
         *  5 astore_2
         */
        String s2 = "b";
        /**
         *  6 ldc #16 <ab>
         *  8 astore_3
         */
        String s3 = "ab"; // 常量池中
        /**
         *  9 new #9 <java/lang/StringBuilder>
         * 12 dup
         * 13 invokespecial #10 <java/lang/StringBuilder.<init>>
         * 16 aload_1
         * 17 invokevirtual #11 <java/lang/StringBuilder.append>
         * 20 aload_2
         * 21 invokevirtual #11 <java/lang/StringBuilder.append>
         * 24 invokevirtual #12 <java/lang/StringBuilder.toString>
         * 27 astore 4
         *
         * StringBuilder.toString  约等于  new String("ab");
         * 在jdk5.0之前使用的是StringBuffer
         */
        String s4 = s1 + s2; // 堆中 (拼接实际上是调用的StringBuilder.append)
        System.err.println(s3 == s4); // false
    }

    @Test
    public void test4() {
        final String s1 = "a";
        final String s2 = "b";
        String s3 = "ab";
        /**
         * 字符串拼接操作不一定使用的是 StringBuilder
         * 如果拼接符号左右两边都是 字符串常量 或 常量引用
         * 则仍然使用编译期优化，非 StringBuilder 拼接的方式
         */
        String s4 = s1 + s2;
        System.err.println(s3 == s4); // true
    }

    @Test
    public void test5() {
        String s1 = "javaEEhadoop";
        String s2 = "javaEE";
        String s3 = s2 + "hadoop";
        System.err.println(s1 == s3); // false

        final String s4 = "javaEE"; // 常量
        String s5 = s4 + "hadoop";
        System.err.println(s1 == s5); // true
    }

    /**
     * 通过StringBuilder的append()的方式添加字符串的效率
     * 要远高于使用加号( + )拼接的方式
     *
     * ①StringBuilder的append()的方式：始终只创建了一个StringBuilder对象；
     * ②使用加号( + )拼接的方式：创建多个StringBuilder和String对象
     * ③使用加号( + )拼接的方式：由于内存中创建多个StringBuilder和String对象，内存占用更大，如果进行GC，时间更长
     *
     * 改进：
     * 在实际开发中，如果基本确定要拼接字符串长度，可以使用new StringBuilder(int maxLen)，
     * 以避免StringBuilder在拼接字符串过程中扩容
     *
     */
    @Test
    public void test6() {
        long start = System.currentTimeMillis();
        concat(100000); // 21136ms
//        append(100000); // 7ms
        long end = System.currentTimeMillis();
        System.err.println("花费时间：" + (end - start) + "ms");
    }

    public void concat(int highLevel) {
        String src = "";
        for (int i = 0; i < highLevel; i++) {
            src = src + "a"; // 每次循环都会创建一个 StringBuilder, String
        }
    }

    public void append(int highLevel) {
        // 只需要创建一个 StringBuilder 对象
        StringBuilder src = new StringBuilder();
        for (int i = 0; i < highLevel; i++) {
            src.append("a");
        }

    }
}
